/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */
#include "FractalGenerator.h"

namespace PQEngine
{

	float MidDisplacementGenerator::getSquareMeanValue(int i,int j,int stride,int size,float *data)
	{
		return ((float) (data[((i-stride)*size) + j-stride] +data[((i-stride)*size) + j+stride] +
			data[((i+stride)*size) + j-stride] +data[((i+stride)*size) + j+stride]) * 0.25f);
	}

	float MidDisplacementGenerator::getDiamondMeanValue (int i, int j, int stride,int size, int subSize, float *data)
	{
		if (i == 0){
			return ((float) (data[(i*size) + j-stride] +
					 data[(i*size) + j+stride] +
					 data[((subSize-stride)*size) + j] +
					 data[((i+stride)*size) + j]) * .25f);
		}else if (i == size-1){
			return ((float) (data[(i*size) + j-stride] +
					 data[(i*size) + j+stride] +
					 data[((i-stride)*size) + j] +
					 data[((0+stride)*size) + j]) * .25f);
		}else if (j == 0){
			return ((float) (data[((i-stride)*size) + j] +
					 data[((i+stride)*size) + j] +
					 data[(i*size) + j+stride] +
					 data[(i*size) + subSize-stride]) * .25f);
		}else if (j == size-1){
			return ((float) (data[((i-stride)*size) + j] +
					 data[((i+stride)*size) + j] +
					 data[(i*size) + j-stride] +
					 data[(i*size) + 0+stride]) * .25f);
		}else{
			return ((float) (data[((i-stride)*size) + j] +
					 data[((i+stride)*size) + j] +
					 data[(i*size) + j-stride] +
					 data[(i*size) + j+stride]) * 0.25f);
		}
	}


	void MidDisplacementGenerator::generate(float* data, int size, int seedValue, float heightScale, float h)
	{
		int i, j;
		int stride;
		int subSize;
		float ratio, scale;

		if (!MathUtil::powerOfTwo(size) || (size==1)) {
			assert(!"Shouldn't be");
			return;
		}

		srand(seedValue);
		subSize = size;
		size++;

		ratio = (float)pow(2.0f,-h);
		scale = heightScale * ratio;

		stride = subSize / 2;
		data[0] = data[(subSize*size)] =data[(subSize*size)+subSize] = data[subSize] = 0.0f;

		while (stride) {
			for (i=stride; i<subSize; i+=stride) {
				for (j=stride; j<subSize; j+=stride) {
					data[(i * size) + j] =scale * MathUtil::randFloat(0.5f) +getSquareMeanValue(i, j, stride, size, data);
					j += stride;
				}
				i += stride;
			}

			bool odd = false;
			for (i=0; i<subSize; i+=stride) {
				odd=!odd;
				for (j=0; j<subSize; j+=stride) {
					if ((odd) && !j) j+=stride;
					data[(i * size) + j] =scale * MathUtil::randFloat(0.5f) + getDiamondMeanValue(i, j, stride, size, subSize, data);
					if (i==0)
						data[(subSize*size) + j] =data[(i * size) + j];
					if (j==0)
						data[(i*size) + subSize] =data[(i * size) + j];

					j+=stride;
				}
			}

			scale *= ratio;
			stride >>= 1;
		}

	}

	void FractalGenerator::generate(int size, int seedValue, float heightScale, float h)
	{
		if(!_impl){
			_impl=new MidDisplacementGenerator();
		}
		if(_data){
			delete _data;
			_data=0;
		}
		_size=size+1;
		_seg=size;
		_capacity=_size*_size;
		_data=new float[_capacity];
		_heightScale=heightScale;
		_impl->generate(_data,size,seedValue,heightScale,h);
	}

	float FractalGenerator::get(int i,int j)
	{
		assert(_data);
		int index=(i*_size) + j;
		assert(index<_capacity);
		
		return _data[index];
	}

}

